﻿using ChoMy.Areas.Admin.Models;
using ChoMy.Constants;
using ChoMy.Helpers;
using ChoMy.Models;
using ChoMy.Models.Data;
using PagedList;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Web;
using System.Web.Mvc;

namespace ChoMy.Areas.Admin.Controllers
{
    public class CategoryController : Controller
    {
        private ChoMyContext db = new ChoMyContext();
        private StringHelper strHp = new StringHelper();
        private FileHelper fileHp = new FileHelper();
        private CategoryHelper cateHp = new CategoryHelper();
        private string title = "danh mục";

        public CategoryController()
        {
            ViewBag.Title = title;
        }

        [Authorize(Roles = "Admin, Category Read Public, Category Read Private")]
        public ActionResult Index(bool? isTrash, string search, string sortOrder, int? page, int? pageSize)
        {
            var sortList = new List<string> { "Name", "Level", "DateModified", "DateCreated", "Products.Count" };
            var pageSizeList = new List<int> { 10, 25, 50, 100 };
            int pageNumber = page ?? 1;

            pageSize = pageSize ?? 10;
            sortOrder = sortOrder ?? "DateCreated_desc";
            isTrash = isTrash ?? false;

            ViewBag.IsTrash = isTrash;
            ViewBag.PageSize = pageSize;
            ViewBag.PageSizeDDL = new SelectList(pageSizeList, pageSize);
            ViewBag.ReturnListUrl = Request.Url.AbsoluteUri;

            IQueryable<Category> query = db.Categories;

            if (User.IsInRole("Category Read Private"))
            {
                query = query.Where(x => x.CreatedBy == User.Identity.Name);
            }

            ViewBag.TrashCount = query.Count(x => x.IsDelete);
            ViewBag.ListCount = query.Count(x => !x.IsDelete);

            if (!String.IsNullOrWhiteSpace(search))
            {
                page = 1;

                query = Search(query, search.Trim());
            }

            query = Sort(query, sortOrder, sortList);

            query = query.Where(x => x.IsDelete == isTrash);

            return View(query.ToPagedList(pageNumber, (int)pageSize));
        }

        [Authorize(Roles = "Admin, Category Read Public, Category Read Private")]
        public ActionResult _Details(int id)
        {
            Category category = db.Categories.Find(id);

            if (User.IsInRole("Category Read Private") && category.CreatedBy != User.Identity.Name)
            {
                return null;
            }

            return PartialView(category);
        }

        [Authorize(Roles = "Admin, Category Create")]
        public ActionResult Create(string returnListUrl)
        {
            ViewBag.CategoryTreeView = CategoryTreeView(null);
            ViewBag.PropertyTypeDDL = PropertyTypeSelectList(0);
            ViewBag.ReturnListUrl = returnListUrl;

            return View();
        }

        [HttpPost]
        [Authorize(Roles = "Admin, Category Create")]
        [ValidateAntiForgeryToken]
        public ActionResult Create(CategoryCreator model, string returnListUrl)
        {
            if (ModelState.IsValid)
            {
                var category = new Category();
                category.Name = model.Name.Trim();
                category.NameId = strHp.Unsigned(category.Name);
                category.DisplayName = model.DisplayName.Trim();
                category.ParentId = model.ParentId;

                if (model.ParentId != null)
                {
                    category.Level = db.Categories.Find(model.ParentId).Level + 1;
                }

                if (String.IsNullOrWhiteSpace(fileHp.Valid(model.Image)))
                {
                    category.ImageUrl = fileHp.Upload(model.Image, model.Name, "/upload/image/category");
                }

                db.Categories.Add(category);
                db.SaveChanges();

                // CategoryPropertyType
                foreach (PropertyType pType in db.PropertyTypes)
                {
                    if (Request["cb" + pType.Name].Contains("true"))
                    {
                        var cpt = new CategoryPropertyType();
                        cpt.PropertyTypeId = pType.Id;
                        cpt.CategoryId = category.Id;

                        db.CategoryPropertyTypes.Add(cpt);
                    }
                }

                db.SaveChanges();

                SaveHistory("Create", category.Name);
                AlertHandling("Create", category.Name);

                if (!String.IsNullOrWhiteSpace(returnListUrl))
                {
                    return Redirect(returnListUrl);
                }

                return RedirectToAction("Index");
            }

            ViewBag.ReturnListUrl = returnListUrl;
            ViewBag.PropertyTypeDDL = PropertyTypeSelectList(0);

            return View(model);
        }

        [Authorize(Roles = "Admin, Category Edit Public, Category Edit Private")]
        public ActionResult Edit(int? id, string returnListUrl)
        {
            if (id == null)
            {
                return RedirectToAction("BadRequest", "Error");
            }

            Category category = db.Categories.Find(id);

            if (category == null)
            {
                return RedirectToAction("NotFound", "Error");
            }

            if (User.IsInRole("Category Edit Private") && category.CreatedBy != User.Identity.Name)
            {
                return RedirectToAction("Unauthorized", "Error");
            }

            var model = new CategoryEditor();
            model.Id = category.Id;
            model.Name = category.Name;
            model.DisplayName = category.DisplayName;

            ViewBag.PropertyTypeDDL = PropertyTypeSelectList(category.Id);
            ViewBag.ReturnListUrl = returnListUrl;

            return View(model);
        }

        [HttpPost]
        [Authorize(Roles = "Admin, Category Edit Public, Category Edit Private")]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(CategoryEditor model, string returnListUrl)
        {
            if (ModelState.IsValid)
            {
                Category category = db.Categories.Find(model.Id);

                if (User.IsInRole("Category Edit Private") && category.CreatedBy != User.Identity.Name)
                {
                    return RedirectToAction("Unauthorized", "Error");
                }

                category.Name = model.Name.Trim();
                category.NameId = strHp.Unsigned(category.Name);
                category.DisplayName = model.DisplayName.Trim();
                category.DateModified = DateTime.Now;
                category.ModifiedBy = User.Identity.Name;

                if (String.IsNullOrWhiteSpace(fileHp.Valid(model.Image)))
                {
                    fileHp.Delete(category.ImageUrl);
                    category.ImageUrl = fileHp.Upload(model.Image, category.Name, "/upload/image/category");
                }

                db.Entry(category).State = EntityState.Modified;
                db.SaveChanges();

                // CategoryPropertyType
                db.CategoryPropertyTypes.RemoveRange(category.CategoryPropertyTypes);

                foreach (PropertyType pType in db.PropertyTypes)
                {
                    if (Request["cb" + pType.Name].Contains("true"))
                    {
                        var cpt = new CategoryPropertyType();
                        cpt.PropertyTypeId = pType.Id;
                        cpt.CategoryId = model.Id;

                        db.CategoryPropertyTypes.Add(cpt);
                    }
                }

                db.SaveChanges();

                SaveHistory("Edit", category.Name);
                AlertHandling("Edit", category.Name);

                if (!String.IsNullOrWhiteSpace(returnListUrl))
                {
                    return Redirect(returnListUrl);
                }

                return RedirectToAction("Index");
            }

            ViewBag.PropertyTypeDDL = PropertyTypeSelectList(model.Id);
            ViewBag.ReturnListUrl = returnListUrl;

            return View(model);
        }

        [HttpPost]
        [Authorize(Roles = "Admin, Category Delete Public, Category Delete Private")]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(int id, string returnListUrl)
        {
            Category category = db.Categories.Find(id);

            if (User.IsInRole("Category Delete Private") && category.CreatedBy != User.Identity.Name)
            {
                return RedirectToAction("Unauthorized", "Error");
            }

            string categoryName = category.Name;

            List<Category> childs = cateHp.Child(category.Id);
            childs.Insert(0, category);
            childs.Reverse();

            foreach (Category cate in childs)
            {
                foreach (String url in cate.Products.SelectMany(x => x.Images).Select(x => x.Url))
                {
                    fileHp.Delete(url);
                }

                fileHp.Delete(cate.ImageUrl);

                db.Categories.Remove(cate);
            }

            db.Categories.Remove(category);
            db.SaveChanges();

            SaveHistory("Delete", categoryName);
            AlertHandling("Delete", categoryName);

            if (!String.IsNullOrWhiteSpace(returnListUrl))
            {
                return Redirect(returnListUrl);
            }

            return RedirectToAction("Index", new { isTrash = true });
        }

        [HttpPost]
        [Authorize(Roles = "Admin, Category Recycle Public, Category Recycle Private")]
        public ActionResult Recycle(int id)
        {
            Category category = db.Categories.Find(id);

            if (User.IsInRole("Category Recycle Private") && category.CreatedBy != User.Identity.Name)
            {
                return Json(-1, JsonRequestBehavior.AllowGet);
            }

            category.IsDelete = category.IsDelete ? false : true;
            category.DateModified = DateTime.Now;
            category.ModifiedBy = User.Identity.Name;

            db.Entry(category).State = EntityState.Modified;
            db.SaveChanges();

            if (category.IsDelete)
            {
                SaveHistory("Recycle-Delete", category.Name);
            }
            else
            {
                SaveHistory("Recycle-Restore", category.Name);
            }

            return Json(category.IsDelete ? 1 : 0, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult CheckName(string name, int? id)
        {
            string tmp = strHp.Unsigned(name);

            Category category = db.Categories.FirstOrDefault(x => x.NameId == tmp && x.Id != id);

            if (category != null)
            {
                if (!category.IsDelete)
                {
                    string url = Url.Action("Index", new { search = name, isTrash = false });
                    string result = String.Format("Tên {0} này đã tồn tại. <a href='{1}'>Chi tiết</a>.", title, url);

                    return Json(result, JsonRequestBehavior.AllowGet);
                }
                else
                {
                    string url = Url.Action("Index", new { search = name, isTrash = true });
                    string result = String.Format("Tên {0} này đã tạm thời bị xóa. <a href='{1}'>Chi tiết</a>.", title, url);

                    return Json(result, JsonRequestBehavior.AllowGet);
                }
            }

            return Json(true, JsonRequestBehavior.AllowGet);
        }

        #region Helpers
        private List<SelectListItem> PropertyTypeSelectList(int categoryId)
        {
            if (categoryId != 0)
            {
                List<int> ids = db.CategoryPropertyTypes
                  .Where(x => x.CategoryId == categoryId)
                  .Select(x => x.PropertyTypeId)
                  .ToList();

                return db.PropertyTypes
                    .OrderBy(x => x.Name)
                    .Select(x => new SelectListItem
                    {
                        Text = x.Name,
                        Selected = ids.Contains(x.Id)
                    }).ToList();
            }
            else
            {
                return db.PropertyTypes
                    .Where(x => !x.IsDelete)
                    .OrderBy(x => x.Name)
                    .Select(x => new SelectListItem
                    {
                        Text = x.Name
                    }).ToList();
            }
        }

        private string CategoryTreeView(int? parentId)
        {
            string html = "";

            List<Category> categories = db.Categories
                .Where(x => x.ParentId == parentId)
                .ToList();

            foreach (var cate in categories)
            {
                Category exists = db.Categories
                    .Where(x => x.ParentId == cate.Id)
                    .FirstOrDefault();

                html += "<li><span data-value='" + cate.Id + "'>" + cate.DisplayName + "</span>";
                if (exists != null)
                {
                    html += "<div class='expander'></div><ul>";
                    html += CategoryTreeView(cate.Id);
                }
            }
            html += "</ul></li>";
            return html;
        }

        private void SaveHistory(string crud, string name)
        {
            string type = "Category";

            DateTime dateTime = DateTime.Now;
            string userName = User.Identity.Name;
            string description = "";

            switch (crud)
            {
                case "Create":
                    description = String.Format("<b>{0}</b> tạo mới {1} <b>{2}</b>", userName, title, name);
                    break;
                case "Edit":
                    description = String.Format("<b>{0}</b> chỉnh sửa {1} <b>{2}</b>", userName, title, name);
                    break;
                case "Delete":
                    description = String.Format("<b>{0}</b> xóa vĩnh viễn {1} <b>{2}</b>", userName, title, name);
                    break;
                case "Recycle-Delete":
                    description = String.Format("<b>{0}</b> xóa tạm thời {1} <b>{2}</b>", userName, title, name);
                    break;
                case "Recycle-Restore":
                    description = String.Format("<b>{0}</b> khôi phục {1} <b>{2}</b>", userName, title, name);
                    break;
            }

            var history = new History();
            history.DateTime = dateTime;
            history.Description = description;
            history.HistoryTypeId = db.HistoryTypes.First(x => x.Name == type).Id;

            db.Histories.Add(history);
            db.SaveChanges();
        }

        private void AlertHandling(string crud, string name)
        {
            string userName = User.Identity.Name;
            string alertMessage = "";

            switch (crud)
            {
                case "Create":
                    alertMessage = String.Format("Bạn vừa tạo mới {0}: <b>{1}</b>", title, name);
                    break;
                case "Edit":
                    alertMessage = String.Format("Bạn vừa chỉnh sửa {0}: <b>{1}</b>", title, name);
                    break;
                case "Delete":
                    alertMessage = String.Format("Bạn vừa xóa vĩnh viễn {0}: <b>{1}</b>", title, name);
                    break;
            }

            if (!String.IsNullOrWhiteSpace(alertMessage))
            {
                Session["alert-message-" + userName] = alertMessage;
                Session["alert-type-" + userName] = "alert-success";
            }
        }

        private IQueryable<Category> Search(IQueryable<Category> query, string search)
        {
            ViewBag.Search = search;

            search = strHp.Unsigned(search);

            query = query.Where(x => x.NameId.Contains(search));

            return query;
        }

        private IQueryable<Category> Sort(IQueryable<Category> query, string sortOrder, List<string> sortList)
        {
            ViewBag.SortOrder = sortOrder;

            foreach (string sort in sortList)
            {
                ViewData[sort + "Sort"] = sortOrder == sort + "_desc" ? sort : sort + "_desc";
            }

            if (sortOrder.Contains("_desc"))
            {
                sortOrder = sortOrder.Replace("_desc", "");
                query = query.OrderBy(sortOrder + " descending");
                ViewData[sortOrder + "Icon"] = DataTableConstant.DescSortIcon;
            }
            else
            {
                query = query.OrderBy(sortOrder);
                ViewData[sortOrder + "Icon"] = DataTableConstant.AscSortIcon;
            }

            return query;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }

            base.Dispose(disposing);
        }
        #endregion
    }
}